/*
  装饰器 - -  在其他语言中 这个都不算是新东西
    装饰器：一种特殊的类型声明，可以附加在类的声明、方法、属性上，可以修改类的行为

    通俗的将装饰器本质上就是一个方法，可以注入到类、方法、属性参数来扩展类、方法、属性的功能

    常见的装饰器有：类装饰器、属性装饰器、方法装饰器、参数装饰器

    装饰器的写法：普通装饰器（无法传参）  、  装饰器工厂（可传参）

    装饰器是过去几年中JS最大的成就之一，已经是ES7的标准特性之一
*/

// 1.类装饰器

// 1.1 普通装饰器  （不能传参）
function modification1(target: any): void {
  console.log(target) // 打印出 [Function: bookStore]  bookstore 类 当前类

  /*
    因为target表示的是当前类  直接通过类名点成员们访问的应该是静态方法  而这个类没有设置静态
      方法  所以放回的自然是 undefined   实例属性和方法只有通过new出来的实例才可以调用
  */

  console.log(target.author) // undefined
  console.log(target.books) // undefined

  // 在当前类的原型上可以扩展属性和方法
  target.prototype.age = 22
}

@modification1 //  调用装饰器的时候这里一定不能加分号
class bookStore {
  public author: string = 'Jimmy'
  private books: string[] = ['htmml']
  private getBook(): void {
    console.log(this.books)
  }
  constructor(book: string) {
    // 构造函数不需要指定返回值类型
    this.books.push(book)
  }
}

let JimmyBook: any = new bookStore('js') // 给类指定一个any类型就不会报错
console.log(JimmyBook.age)

// 1.2 装饰器工厂 （可以传递参数）
// 装饰器工程无非就是在 基础装饰器的 里面 再嵌套一个方法 - - 嵌套的方法可以获取参数
// 但是要知道 被嵌套的 方法是拿来传参的  父装饰器才是获取对象的

function modification2(value: string): any {
  // 嵌套方法获取参数
  return function paramsFun(target: any): void {
    console.log(target) // movie类
    console.log(value) // node.js大法

    target.prototype['msg'] = value
  }
}

@modification2('node.js大法')
class movie {
  public auther: string = 'xuexue'
  private movieList: string[] = []
  constructor(movie: string) {
    this.movieList.push(movie)
  }
}

let JimmyMove: any = new movie('TypeScript全套教程')

console.log(JimmyMove.msg) //  node.js大法

/*
  总结：
    装饰器真的超级好用 可以让我们在不修改一个类内部结构的情况下， 对类的 属性 方法进行扩展  
*/

// 2.属性修饰器
//  属性装饰器表达式会在运行时当做函数被调用，传入下列两个参数
// 1： 对于静态成员来说是类的构造函数  对于实例成员来说是类的原型对象
// 2： 成员的名字

/*
  属性装饰器默认是工厂模式  一样的 最外面一层的函数是获取返回值的  嵌套的才是原型对象  我们就可以根据原型对象来对类的属性进行一些修改
*/

function attribute1(params: string): any {
  console.log(params) // www.baidu.com
  return function (value1: any, value2: any) {
    console.log('属性装饰器', value1) // course 原型方法
    console.log('属性装饰器', value2) // 使用修饰器的那个属性 name  默认会拿到距离最近的一个属性
    value1[value2] = params
  }
}

class course {
  @attribute1('http://www.baidu.com') // 注意注意：  属性装饰器只能获取距离这个装饰器最近的一个属性
  public name: string | undefined
  public school: string | undefined
  private sid: number = 173117002
  getData(): void {
    console.log(this.name)
  }
}

let java = new course()
console.log(java.name) //  http://www.baidu.com
